<?php
//微信方法
class ext_weixin{
    public $config = ""; //微信配置

    public function __construct($config = "") {
        $config = json_encode($config);
        if (!empty($config)) $this->config = json_decode($config);
    }

    //跳转到微信登录
    public function wxLogin($back_url,$scope = "snsapi_base"){
        return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".$this->config->APPID."&redirect_uri=".urlencode($back_url)."&response_type=code&scope=".$scope."&state=1#wechat_redirect";
    }

    //微信jsapi支付
    public function toPay($openId,$money,$title,$order,$notify,$pay_type = "JSAPI"){
        $mchid = $this->config->MCHID;
        $appid = $this->config->APPID;
        $appKey = $this->config->APPSECRET;
        $apiKey = $this->config->KEY;
        if ($pay_type == "APP") $appid = $this->config->OPENAPPID;

        $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey);
        $outTradeNo = $order;
        $payAmount = $money;
        $orderName = $title;
        $notifyUrl = $notify;
        $payTime = time();      //付款时间
        $jsApiParameters = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$orderName,$notifyUrl,$payTime,$pay_type);
        return json_encode($jsApiParameters);
    }

    //微信支付回调
    public function notify($pay_type = "JSAPI"){
        $mchid = $this->config->MCHID;
        $appid = $this->config->APPID;
        $appKey = $this->config->APPSECRET;
        $apiKey = $this->config->KEY;
        if ($pay_type == "APP") $appid = $this->config->OPENAPPID;

        $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey);
        return $wxPay->notify();
    }

    //退款
    public function refund($tradeNo,$refundAmount){
        $mchid = $this->config->MCHID;
        $appid = $this->config->APPID;
        $apiKey = $this->config->KEY;
        $refundNo = 'refund_'.uniqid();

        $wxPay = new WxpayService($mchid,$appid,$apiKey);
        $result = $wxPay->doRefund($refundAmount, $refundAmount, $refundNo, $tradeNo,"");
        if($result===true) return true;
        return false;
    }

    //微信授权
    public function wxAuth($code){
        //获取微信授权
        if(empty($code)) return array();
        $back = $this->vget("https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$this->config->APPID."&secret=".$this->config->APPSECRET."&code=".$code."&grant_type=authorization_code");
        $back = json_decode($back,true);
        $back = $this->vget("https://api.weixin.qq.com/sns/userinfo?access_token=".$back['access_token']."&openid=".$back['openid']);
        $back = json_decode($back,true);
        if (empty($back['openid'])) return array();
        return $back;  
    }

    //微信分享
    public function wxShare($shareTitle,$shareTxt,$shareUrl,$imgUrl,$success = "",$cancel = ""){
        $token = $this->getToken($this->config->APPID,$this->config->APPSECRET,0);
        $shareToken = $this->getShareToken($token);
        if (empty($shareToken)){
            $token = $this->getToken($this->config->APPID,$this->config->APPSECRET,1);
            $shareToken = $this->getShareToken($token);
        }
        $time = time();
        $wxstr = "Myxf";
        $signature = sha1("jsapi_ticket=" . $shareToken. "&noncestr=".$wxstr."&timestamp=" .$time. "&url=".$_SERVER["HTTP_REFERER"]);
        $html = "document.write(\"<script language='javascript' src='https://res.wx.qq.com/open/js/jweixin-1.0.0.js'></script>\");function wxint(wxconfig){wx.config({debug: false,appId: '".$this->config->APPID."',timestamp: ".$time.",nonceStr: '".$wxstr."',signature: '".$signature."',jsApiList: ['checkJsApi', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice']});wx.ready(function(){wx.onMenuShareTimeline({title: wxconfig.shareTitle,link: wxconfig.shareUrl,imgUrl: wxconfig.imgUrl,success: function () { ".$success."},cancel: function () { ".$cancel."}});wx.onMenuShareAppMessage({title: wxconfig.shareTitle,desc: wxconfig.shareTxt,link: wxconfig.shareUrl,imgUrl: wxconfig.imgUrl,type: '',dataUrl: '',success: function () { ".$success."},cancel: function () { ".$cancel."}});wx.onMenuShareQQ({title: wxconfig.shareTitle,desc: wxconfig.shareTxt,link: wxconfig.shareUrl,imgUrl: wxconfig.imgUrl,success: function () {".$success."},cancel: function () { ".$cancel."}});wx.onMenuShareWeibo({title: wxconfig.shareTitle,desc: wxconfig.shareTxt,link: wxconfig.shareUrl,imgUrl: wxconfig.imgUrl,success: function () { ".$success."},cancel: function () { ".$cancel."}});wx.error(function(res){alert(res.errMsg);});wx.checkJsApi({jsApiList: ['getNetworkType','previewImage'],success: function (res) {}});});}";
        return $html;
    }

    //微信分享token
    private function getShareToken($token,$re = 0){
        $path = dirname(__FILE__);
        $filename = md5("share".$secret).".txt";
        if (filemtime($path."/".$filename)==false || (time()-filemtime($path."/".$filename))>6780 || file_get_contents($path."/".$filename)=="" || $re==1){
            $token = $this->vget("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=".$token."&type=jsapi");
            $token = json_decode($token,true);
            $token = $token['ticket'];
            $fp = fopen($path."/".$filename, "w");
            fwrite($fp, $token);
            fclose($fp);
        }else{
            $token = file_get_contents($path."/".$filename);
        }
        return $token;
    }

    private function getToken($appid,$secret,$re = 0){
        $path = dirname(__FILE__);
        $filename = md5($secret).".txt";
        if (filemtime($path."/".$filename)==false || (time()-filemtime($path."/".$filename))>6780 || file_get_contents($path."/".$filename)=="" || $re==1){
            $token = $this->vget("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$appid."&secret=".$secret);
            $token = json_decode($token,true);
            $token = $token['access_token'];
            $fp = fopen($path."/".$filename, "w");
            fwrite($fp, $token);
            fclose($fp);
        }else{
            $token = file_get_contents($path."/".$filename);
        }
        return $token;
    }

    //模板消息
    public function postwx($data){
        $appid = $this->config->APPID;
        $secret = $this->config->APPSECRET;
        $token = $this->gettoken($appid,$secret);
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token;
        return $this->vpost($url,$data);
    }

    //微信红包
    public function wxRed($openid,$money,$shop_name,$wishing = "",$act_name = "保证金退还"){
        $wxPay = new WxpayService();
        $wxHongBaoHelper = array();
        $wxHongBaoHelper['nonce_str'] = $wxPay->createNonceStr();
        $wxHongBaoHelper['mch_billno'] = $this->config->MCHID.date('YmdHis').rand(1000, 9999);
        $wxHongBaoHelper['mch_id'] = $this->config->MCHID;
        $wxHongBaoHelper['wxappid'] = $this->config->APPID;
        $wxHongBaoHelper['send_name'] = $shop_name;
        $wxHongBaoHelper['re_openid'] = $openid;
        $wxHongBaoHelper['total_amount'] = $money*100;
        $wxHongBaoHelper['total_num'] = 1;
        $wxHongBaoHelper['wishing'] = $wishing;
        $wxHongBaoHelper['client_ip'] = "127.0.0.1";
        $wxHongBaoHelper['act_name'] = $act_name;
        $wxHongBaoHelper['remark'] = "点击领取红包";
        $wxHongBaoHelper['sign'] = $wxPay->getSign($wxHongBaoHelper,$this->config->KEY);
        $postXml = $wxPay->arrayToXml($wxHongBaoHelper);
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack';
        $responseXml = $this->vpost($url, $postXml,[],true);
        $responseObj = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $responseObj;
    }

    //curl get请求
    public function vget($url){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
        curl_setopt($curl, CURLOPT_TIMEOUT, 30);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $tmpInfo = curl_exec($curl);
        if (curl_errno($curl)) {
           return '';
        }
        curl_close($curl);
        return $tmpInfo;
    }

    public function vpost($url,$data,$header = array(),$cert = false){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
        if ($cert){
            curl_setopt($curl, CURLOPT_SSLCERT,dirname(__FILE__).DIRECTORY_SEPARATOR.'apiclient_cert.pem');
            curl_setopt($curl, CURLOPT_SSLKEY,dirname(__FILE__).DIRECTORY_SEPARATOR.'apiclient_key.pem');
        }
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_TIMEOUT, 30);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $tmpInfo = curl_exec($curl);
        if (curl_errno($curl)) {
           return '';
        }
        curl_close($curl);
        return $tmpInfo;
    }

}

class WxpayService
{
    protected $mchid;
    protected $appid;
    protected $appKey;
    protected $apiKey;
    public $data = null;
    public function __construct($mchid, $appid, $appKey,$key)
    {
        $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
        $this->appid = $appid; //微信支付申请对应的公众号的APPID
        $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key
        $this->apiKey = $key;   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
    }

    public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='')
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        $unified = array(
            'appid' => $config['appid'],
            'mch_id' => $config['mch_id'],
            'nonce_str' => self::createNonceStr(),
            'total_fee' => floatval($totalFee) * 100,       //订单金额   单位 转为分
            'refund_fee' => floatval($refundFee) * 100,       //退款金额 单位 转为分
            'sign_type' => 'MD5',           //签名类型 支持HMAC-SHA256和MD5，默认为MD5
            'transaction_id'=>$wxOrderNo,               //微信订单号
            'out_trade_no'=>$orderNo,        //商户订单号
            'out_refund_no'=>$refundNo,        //商户退款单号
            'refund_desc'=>'退款',     //退款原因（选填）
        );
        $unified['sign'] = self::getSign($unified, $config['key']);
        $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified));
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($unifiedOrder === false) {
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            die($unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            die($unifiedOrder->err_code);
        }
        return true;
    }

    public function notify()
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        $postStr = file_get_contents('php://input');
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);        
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($postObj === false) {
            die('parse xml error');
        }
        if ($postObj->return_code != 'SUCCESS') {
            die($postObj->return_msg);
        }
        if ($postObj->result_code != 'SUCCESS') {
            die($postObj->err_code);
        }
        $arr = (array)$postObj;
        unset($arr['sign']);
        if (self::getSign($arr, $config['key']) == $postObj->sign) {
            return $arr;
        }
    }
    
    private function ToUrlParams($urlObj)
    {
        $buff = "";
        foreach ($urlObj as $k => $v)
        {
            if($k != "sign") $buff .= $k . "=" . $v . "&";
        }
        $buff = trim($buff, "&");
        return $buff;
    }
   
    public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp ,$pay_type)
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        //$orderName = iconv('GBK','UTF-8',$orderName);
        $unified = array(
            'appid' => $config['appid'],
            'attach' => 'pay',             //商家数据包，原样返回，如果填写中文，请注意转换为utf-8
            'body' => $orderName,
            'mch_id' => $config['mch_id'],
            'nonce_str' => self::createNonceStr(),
            'notify_url' => $notifyUrl,
            'openid' => $openid,            //rade_type=JSAPI，此参数必传
            'out_trade_no' => $outTradeNo,
            'spbill_create_ip' => '127.0.0.1',
            'total_fee' => intval($totalFee * 100),       //单位 转为分
            'trade_type' => $pay_type,
        );

        $unified['sign'] = self::getSign($unified, $config['key']);
        $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);     
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($unifiedOrder === false) {
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            die($unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            die($unifiedOrder->err_code);
        }
        if ($pay_type == "JSAPI"){
            $arr = array(
                "appId" => $config['appid'],
                "timeStamp" => "$timestamp",        //这里是字符串的时间戳，不是int，所以需加引号
                "nonceStr" => self::createNonceStr(),
                "package" => "prepay_id=" . $unifiedOrder->prepay_id,
                "signType" => 'MD5',
            );
            $arr['paySign'] = self::getSign($arr, $config['key']);
        }elseif ($pay_type == "APP") {
            $arr = array(
                "appid" => $config['appid'],
                "partnerid" => $config['mch_id'],
                "prepayid" => "".$unifiedOrder->prepay_id,
                "package" => "Sign=WXPay",
                "noncestr" => self::createNonceStr(),
                "timestamp" => "$timestamp",        //这里是字符串的时间戳，不是int，所以需加引号
            );
            $arr['sign'] = self::getSign($arr, $config['key']);
        }
        return $arr;
    }

    public static function curlGet($url = '', $options = array())
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    public static function curlPost($url = '', $postData = '', $options = array())
    {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if($url=="https://api.mch.weixin.qq.com/secapi/pay/refund"){
            curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/upload/file/apiclient_cert.pem');
            curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/upload/file/apiclient_key.pem');
        }
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    public static function createNonceStr($length = 16)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    public static function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    public static function getSign($params, $key)
    {
        ksort($params, SORT_STRING);
        $unSignParaString = self::formatQueryParaMap($params, false);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }
    
    protected static function formatQueryParaMap($paraMap, $urlEncode = false)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (null != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }
}
?>